package com.yctan.single;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 处理连接请求
 * @author 谭永超
 */
public class TCPReactor implements Runnable {

    private final ServerSocketChannel ssc;
    private final Selector selector;

    /**
     * 创建selector,serverSocketChannel
     * 绑定端口及接收请求方法
     * @param port
     * @throws IOException
     */
    public TCPReactor(int port) throws IOException {
        selector = Selector.open();
        ssc = ServerSocketChannel.open();
        InetSocketAddress addr = new InetSocketAddress(port);
        //ServerSocketChannel 绑定监听的端口
        ssc.socket().bind(addr);
        // 设置ServerSocketChannel为非阻塞
        ssc.configureBlocking(false);
        // ServerSocketChannel向selector注册OP_ACCEPT事件，然后返回该通道的key
        SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT);
        // 給定key附加一个的Acceptor對象
        sk.attach(new Acceptor(selector, ssc));
    }

    @Override
    public void run() {
        // 再线程被中断时继续执行
        while (!Thread.interrupted()) {
            System.out.println(Thread.currentThread().getName()+"等待新的事件进入" + ssc.socket().getLocalPort() + "...");
            try {
                // 若沒有事件就阻塞 在上一个请求处理完成之后该方法会复位serviceSocketChannel中注册的方法
                if (selector.select() == 0) {
                    System.out.println(Thread.currentThread().getName()+"----------请求被熔断---------");
                    continue;
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //取得所有就绪事件的key
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectedKeys.iterator();
            System.out.println(Thread.currentThread().getName()+"所有就绪事件的key个数：" + selectedKeys.size());
            while (it.hasNext()) {
                // 根据触发的关注事件进行调度
                SelectionKey selectionKey = (SelectionKey) (it.next());
                dispatch(selectionKey);
                it.remove();
            }
        }
    }

    /**
     * name: dispatch(SelectionKey key)
     * description: 调度方法，根據事件綁定的對象開新線程
     */
    private void dispatch(SelectionKey key) {
        // 根据处理事件之前的绑定进行调度执行
        Runnable r = (Runnable) (key.attachment());
        System.out.println(Thread.currentThread().getName()+r);
        if (r != null) {
            r.run();
        }
    }
}
